﻿using System.Collections.ObjectModel;
using System.Globalization;
using System.Text;

namespace LightCAD.Core.Elements
{

    public class Hatch : LcElement
    {
        private readonly ObservableCollection<HatchBoundaryPath> boundaryPaths;
        private HatchPattern pattern;
        private double elevation;
        private bool associative;
        public Hatch()
        {
        }


        public override LcElement Clone()
        {
            var clone = new Hatch();
            clone.Copy(this);
            return clone;
        }

        public override void Copy(LcElement src)
        {
            var hatch = ((Hatch)src);
        }


    }

    public class HatchPatternLineDefinition :
      ICloneable
    {
        #region private fields

        private double angle;
        private Vector2d origin;
        private Vector2d delta;
        private readonly List<double> dashPattern;

        #endregion

        #region constructor

        /// <summary>
        /// Initializes a new instance of the <c>HatchPatternLineDefinition</c> class.
        /// </summary>
        public HatchPatternLineDefinition()
        {
            this.angle = 0.0;
            this.origin = Vector2d.Zero;
            this.delta = Vector2d.Zero;
            this.dashPattern = new List<double>();
        }


        //public void Draw(vdRender render, gPoint _p1, gPoint _p2, double DXOffset)
        //{
        //    double num3 = 0.0;
        //    double pixelSize = render.PixelSize;
        //    num3 = this.CalcSegmentLength();
        //    if ((num3 == 0.0) || (num3 < (pixelSize * 2.0)))
        //    {
        //        render.DrawLine(this, _p1, _p2);
        //        return;
        //    }
        //    gPoint sp = new gPoint(_p1);
        //    double x = DXOffset;
        //    if (x < _p1.x)
        //    {
        //        x = _p1.x - ((_p1.x - x) % num3);
        //    }
        //    if (x > _p1.x)
        //    {
        //        x = (_p1.x + ((x - _p1.x) % num3)) - num3;
        //    }
        //Label_016E:
        //    while (x < _p2.x)
        //    {
        //        foreach (double num4 in this._dashes)
        //        {
        //            x += Math.Abs(num4);
        //            if (x >= _p1.x)
        //            {
        //                gPoint point2;
        //                if (x > _p2.x)
        //                {
        //                    x = _p2.x;
        //                }
        //                if (num4 > 0.0)
        //                {
        //                    point2 = new gPoint(x, _p1.y, _p1.z);
        //                    render.DrawLine(this, sp, point2);
        //                    sp = point2;
        //                }
        //                else if (num4 == 0.0)
        //                {
        //                    point2 = new gPoint(x + pixelSize, _p1.y, _p1.z);
        //                    render.DrawLine(this, sp, point2);
        //                    sp = point2;
        //                }
        //                else
        //                {
        //                    sp = new gPoint(x, _p1.y, _p1.z);
        //                }
        //                if (x >= _p2.x)
        //                {
        //                    goto Label_016E;
        //                }
        //            }
        //        }
        //    }
        //}

        #endregion

        #region public properties

        /// <summary>
        /// Gets or sets the angle of the line.
        /// </summary>
        public double Angle
        {
            get { return this.angle; }
            set
            {
                this.angle = 0;// MathHelper.NormalizeAngle(value);
            }
        }

        /// <summary>
        /// Gets or sets the origin of the line.
        /// </summary>
        public Vector2d Origin
        {
            get { return this.origin; }
            set { this.origin = value; }
        }

        /// <summary>
        /// Gets or sets the local displacements between lines of the same family.
        /// </summary>
        /// <remarks>
        /// The Delta.X value indicates the displacement between members of the family in the direction of the line. It is used only for dashed lines.
        /// The Delta.Y value indicates the spacing between members of the family; that is, it is measured perpendicular to the lines. 
        /// </remarks>
        public Vector2d Delta
        {
            get { return this.delta; }
            set { this.delta = value; }
        }

        /// <summary>
        /// Gets he dash pattern of the line it is equivalent as the segments of a <see cref="Linetype">Linetype</see>.
        /// </summary>
        /// <remarks>
        /// Positive values means solid segments and negative values means spaces (one entry per element).
        /// </remarks>
        public List<double> DashPattern
        {
            get { return this.dashPattern; }
        }

        #endregion

        #region overrides

        /// <summary>
        /// Creates a new HatchPatternLineDefinition that is a copy of the current instance.
        /// </summary>
        /// <returns>A new HatchPatternLineDefinition that is a copy of this instance.</returns>
        public object Clone()
        {
            HatchPatternLineDefinition copy = new HatchPatternLineDefinition
            {
                Angle = this.angle,
                Origin = this.origin,
                Delta = this.delta,
            };

            foreach (double dash in this.dashPattern)
                copy.DashPattern.Add(dash);

            return copy;
        }

        #endregion
    }

    public class HatchPattern :
      ICloneable
    {
        #region private fields

        private readonly string name;
        private readonly List<HatchPatternLineDefinition> lineDefinitions;
        private HatchStyle style;
        private HatchFillType fill;
        private HatchType type;
        private Vector2d origin;
        private double angle;
        private double scale;
        private string description;

        #endregion

        #region constructor

        /// <summary>
        /// Initializes a new instance of the <c>HatchPattern</c> class.
        /// </summary>
        /// <param name="name">Pattern name, always stored as uppercase.</param>
        public HatchPattern(string name)
            : this(name, null, string.Empty)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <c>HatchPattern</c> class.
        /// </summary>
        /// <param name="name">Pattern name, always stored as uppercase.</param>
        /// <param name="description">Description of the pattern (optional, this information is not saved in the DXF file). By default it will use the supplied name.</param>
        public HatchPattern(string name, string description)
            : this(name, null, description)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <c>HatchPattern</c> class.
        /// </summary>
        /// <param name="name">Pattern name, always stored as uppercase.</param>
        /// <param name="lineDefinitions">The definition of the lines that make up the pattern (not applicable in Solid fills).</param>
        public HatchPattern(string name, IEnumerable<HatchPatternLineDefinition> lineDefinitions)
            : this(name, lineDefinitions, string.Empty)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <c>HatchPattern</c> class.
        /// </summary>
        /// <param name="name">Pattern name, always stored as uppercase.</param>
        /// <param name="lineDefinitions">The definition of the lines that make up the pattern (not applicable in Solid fills).</param>
        /// <param name="description">Description of the pattern (optional, this information is not saved in the DXF file). By default it will use the supplied name.</param>
        public HatchPattern(string name, IEnumerable<HatchPatternLineDefinition> lineDefinitions, string description)
        {
            this.name = string.IsNullOrEmpty(name) ? string.Empty : name;
            this.description = string.IsNullOrEmpty(description) ? string.Empty : description;
            this.style = HatchStyle.Normal;
            this.fill = this.name == "SOLID" ? HatchFillType.SolidFill : HatchFillType.PatternFill;
            this.type = HatchType.UserDefined;
            this.origin = Vector2d.Zero;
            this.angle = 0.0;
            this.scale = 1.0;
            this.lineDefinitions = lineDefinitions == null ? new List<HatchPatternLineDefinition>() : new List<HatchPatternLineDefinition>(lineDefinitions);
        }

        #endregion

        #region predefined patterns

        /// <summary>
        /// Solid hatch pattern.
        /// </summary>
        /// <remarks>The predefined pattern values are based on the acad.pat file of AutoCAD.</remarks>
        public static HatchPattern Solid
        {
            get
            {
                HatchPattern pattern = new HatchPattern("SOLID", "Solid fill") { type = HatchType.Predefined };
                // this is the pattern line definition for solid fills as defined in the acad.pat, but it is not needed
                //HatchPatternLineDefinition lineDefinition = new HatchPatternLineDefinition
                //                                                {
                //                                                    Angle = 45,
                //                                                    Origin = Vector2.Zero,
                //                                                    Delta = new Vector2(0.0, 0.125)
                //                                                };
                //pattern.LineDefinitions.Add(lineDefinition);
                return pattern;
            }
        }

        /// <summary>
        /// Lines hatch pattern.
        /// </summary>
        /// <remarks>The predefined pattern values are based on the acad.pat file of AutoCAD.</remarks>
        public static HatchPattern Line
        {
            get
            {
                HatchPattern pattern = new HatchPattern("LINE", "Parallel horizontal lines");
                HatchPatternLineDefinition lineDefinition = new HatchPatternLineDefinition
                {
                    Angle = 0,
                    Origin = Vector2d.Zero,
                    Delta = new Vector2d(0.0, 0.125)
                };
                pattern.LineDefinitions.Add(lineDefinition);
                pattern.type = HatchType.Predefined;
                return pattern;
            }
        }

        /// <summary>
        /// Net or squares hatch pattern.
        /// </summary>
        /// <remarks>The predefined pattern values are based on the acad.pat file of AutoCAD.</remarks>
        public static HatchPattern Net
        {
            get
            {
                HatchPattern pattern = new HatchPattern("NET", "Horizontal / vertical grid");

                HatchPatternLineDefinition lineDefinition = new HatchPatternLineDefinition
                {
                    Angle = 0,
                    Origin = Vector2d.Zero,
                    Delta = new Vector2d(0.0, 0.125)
                };
                pattern.LineDefinitions.Add(lineDefinition);

                lineDefinition = new HatchPatternLineDefinition
                {
                    Angle = 90,
                    Origin = Vector2d.Zero,
                    Delta = new Vector2d(0.0, 0.125)
                };
                pattern.LineDefinitions.Add(lineDefinition);
                pattern.type = HatchType.Predefined;
                return pattern;
            }
        }

        /// <summary>
        /// Dots hatch pattern.
        /// </summary>
        /// <remarks>The predefined pattern values are based on the acad.pat file of AutoCAD.</remarks>
        public static HatchPattern Dots
        {
            get
            {
                HatchPattern pattern = new HatchPattern("DOTS", "A series of dots");
                HatchPatternLineDefinition lineDefinition = new HatchPatternLineDefinition
                {
                    Angle = 0,
                    Origin = Vector2d.Zero,
                    Delta = new Vector2d(0.03125, 0.0625),
                };
                lineDefinition.DashPattern.AddRange(new[] { 0, -0.0625 });
                pattern.LineDefinitions.Add(lineDefinition);
                pattern.type = HatchType.Predefined;
                return pattern;
            }
        }

        #endregion

        #region public properties

        /// <summary>
        /// Gets or sets the hatch pattern name.
        /// </summary>
        public string Name
        {
            get { return this.name; }
        }

        /// <summary>
        /// Gets or sets the hatch description (optional, this information is not saved in the DXF file).
        /// </summary>
        public string Description
        {
            get { return this.description; }
            set { this.description = value; }
        }

        /// <summary>
        /// Gets the hatch style.
        /// </summary>
        /// <remarks>Only normal style is implemented.</remarks>
        public HatchStyle Style
        {
            get { return this.style; }
            internal set { this.style = value; }
        }

        /// <summary>
        /// Gets or sets the hatch pattern type.
        /// </summary>
        public HatchType Type
        {
            get { return this.type; }
            set { this.type = value; }
        }

        /// <summary>
        /// Gets the solid fill flag.
        /// </summary>
        public HatchFillType Fill
        {
            get { return this.fill; }
            internal set { this.fill = value; }
        }

        /// <summary>
        /// Gets or sets the pattern origin.
        /// </summary>
        public Vector2d Origin
        {
            get { return this.origin; }
            set { this.origin = value; }
        }

        /// <summary>
        /// Gets or sets the pattern angle in degrees.
        /// </summary>
        public double Angle
        {
            get { return this.angle; }
            set
            {
                this.angle = 0;// MathHelper.NormalizeAngle(value);
            }
        }

        /// <summary>
        /// Gets or sets the pattern scale (not applicable in Solid fills).
        /// </summary>
        public double Scale
        {
            get { return this.scale; }
            set
            {
                if (value <= 0)
                    throw new ArgumentOutOfRangeException(nameof(value), value, "The scale can not be zero or less.");
                this.scale = value;
            }
        }

        /// <summary>
        /// Gets the definition of the lines that make up the pattern (not applicable in Solid fills).
        /// </summary>
        public List<HatchPatternLineDefinition> LineDefinitions
        {
            get { return this.lineDefinitions; }
        }

        #endregion

        #region public methods

        /// <summary>
        /// Gets the list of hatch pattern names defined in a PAT file.
        /// </summary>
        /// <param name="file">Hatch pattern definitions file.</param>
        /// <returns>List of hatch pattern names contained in the specified PAT file.</returns>
        public static List<string> NamesFromFile(string file)
        {
            List<string> names = new List<string>();
            using (StreamReader reader = new StreamReader(File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite), true))
            {
                while (!reader.EndOfStream)
                {
                    string line = reader.ReadLine();
                    if (line == null)
                        throw new FileLoadException("Unknown error reading PAT file.", file);

                    // every line type definition starts with '*'
                    if (!line.StartsWith("*"))
                        continue;

                    // reading line type name and description
                    int endName = line.IndexOf(',');
                    // the first semicolon divides the name from the description that might contain more semicolons
                    names.Add(line.Substring(1, endName - 1));
                }
            }
            return names;
        }

        /// <summary>
        /// Creates a new hatch pattern from the definition in a PAT file.
        /// </summary>
        /// <param name="file">PAT file where the definition is located.</param>
        /// <param name="patternName">Name of the pattern definition that wants to be read (ignore case).</param>
        /// <returns>A Hatch pattern as defined in the PAT file.</returns>
        public static HatchPattern Load(string file, string patternName)
        {
            HatchPattern pattern = null;

            using (StreamReader reader = new StreamReader(File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite), true))
            {
                while (!reader.EndOfStream)
                {
                    string line = reader.ReadLine();
                    if (line == null)
                        throw new FileLoadException("Unknown error reading pat file.", file);
                    line = line.Trim();

                    // every pattern definition starts with '*'
                    if (!line.StartsWith("*"))
                        continue;

                    // reading pattern name and description
                    int endName = line.IndexOf(','); // the first semicolon divides the name from the description that might contain more semicolons
                    string name = line.Substring(1, endName - 1);
                    string description = line.Substring(endName + 1, line.Length - endName - 1);

                    // remove start and end spaces
                    description = description.Trim();
                    if (!name.Equals(patternName, StringComparison.OrdinalIgnoreCase))
                        continue;

                    // we have found the pattern name, the next lines of the file contains the pattern definition
                    line = reader.ReadLine();
                    if (line == null)
                        throw new FileLoadException("Unknown error reading PAT file.", file);
                    line = line.Trim();

                    pattern = new HatchPattern(name, description);

                    while (!string.IsNullOrEmpty(line) && !line.StartsWith("*") && !line.StartsWith(";"))
                    {
                        string[] tokens = line.Split(',');
                        if (tokens.Length < 5)
                            throw new FileLoadException("The hatch pattern definition lines must contain at least 5 values.", file);

                        double angle = double.Parse(tokens[0], NumberStyles.Float, CultureInfo.InvariantCulture);
                        Vector2d origin = new Vector2d(
                            double.Parse(tokens[1], NumberStyles.Float, CultureInfo.InvariantCulture),
                            double.Parse(tokens[2], NumberStyles.Float, CultureInfo.InvariantCulture));
                        Vector2d delta = new Vector2d(
                            double.Parse(tokens[3], NumberStyles.Float, CultureInfo.InvariantCulture),
                            double.Parse(tokens[4], NumberStyles.Float, CultureInfo.InvariantCulture));

                        HatchPatternLineDefinition lineDefinition = new HatchPatternLineDefinition
                        {
                            Angle = angle,
                            Origin = origin,
                            Delta = delta,
                        };

                        // the rest of the info is optional if it exists define the dash pattern definition
                        for (int i = 5; i < tokens.Length; i++)
                            lineDefinition.DashPattern.Add(double.Parse(tokens[i], NumberStyles.Float, CultureInfo.InvariantCulture));

                        pattern.LineDefinitions.Add(lineDefinition);
                        pattern.Type = HatchType.UserDefined;

                        if (reader.EndOfStream) break;

                        line = reader.ReadLine();
                        if (line == null)
                            throw new FileLoadException("Unknown error reading PAT file.", file);
                        line = line.Trim();
                    }

                    // there is no need to continue parsing the file, the info has been read
                    break;
                }
            }

            return pattern;
        }

        /// <summary>
        /// Saves the current linetype to the specified file, if the file does not exist it creates a new one.
        /// </summary>
        /// <param name="file">File where the current linetype will be saved.</param>
        public void Save(string file)
        {
            StringBuilder sb = new StringBuilder();

            sb.AppendLine(string.Format("*{0},{1}", this.Name, this.description));

            foreach (HatchPatternLineDefinition def in this.lineDefinitions)
            {
                sb.Append(string.Format("{0},{1},{2},{3},{4}",
                    def.Angle.ToString(CultureInfo.InvariantCulture),
                    def.Origin.X.ToString(CultureInfo.InvariantCulture),
                    def.Origin.Y.ToString(CultureInfo.InvariantCulture),
                    def.Delta.X.ToString(CultureInfo.InvariantCulture),
                    def.Delta.Y.ToString(CultureInfo.InvariantCulture)));
                foreach (double d in def.DashPattern)
                {
                    sb.Append(string.Format(",{0}", d.ToString(CultureInfo.InvariantCulture)));
                }
                sb.Append(Environment.NewLine);
            }

            File.AppendAllText(file, sb.ToString());
        }

        #endregion

        #region ICloneable

        public virtual object Clone()
        {
            HatchPattern copy = new HatchPattern(this.name, this.description)
            {
                Style = this.style,
                Fill = this.fill,
                Type = this.type,
                Origin = this.origin,
                Angle = this.angle,
                Scale = this.scale,
            };

            foreach (HatchPatternLineDefinition def in this.lineDefinitions)
                copy.LineDefinitions.Add((HatchPatternLineDefinition)def.Clone());

            return copy;
        }

        #endregion
    }
    public class EntityObject
    {

    }
    public class HatchBoundaryPath :
      ICloneable
    {
        #region Hatch boundary path edge classes

        /// <summary>
        /// Specifies the type of HatchBoundaryPath.Edge.
        /// </summary>
        public enum EdgeType
        {
            Polyline = 0,
            Line = 1,
            Arc = 2,
            Ellipse = 3,
            Spline = 4
        }

        /// <summary>
        /// Base class for all types of HatchBoundaryPath edges.
        /// </summary>
        public abstract class Edge :
            ICloneable
        {
            /// <summary>
            /// Gets the HatchBoundaryPath edge type
            /// </summary>
            public readonly EdgeType Type;

            protected Edge(EdgeType type)
            {
                this.Type = type;
            }

            /// <summary>
            /// Converts the actual edge to its entity equivalent.
            /// </summary>
            /// <returns>An EntityObject equivalent to the actual edge.</returns>
            public abstract EntityObject ConvertTo();

            /// <summary>
            /// Clones the actual edge.
            /// </summary>
            /// <returns>A copy of the actual edge.</returns>
            public abstract object Clone();
        }

        /// <summary>
        /// Represents a polyline edge of a HatchBoundaryPath.
        /// </summary>
        public class Polyline :
            Edge
        {
            /// <summary>
            /// Gets or sets the list of polyline vertexes.
            /// </summary>
            /// <remarks>
            /// The position of the vertex is defined by the X and Y coordinates, the Z value represents the bulge at that vertex.
            /// </remarks>
            public Vector2d[] Vertexes;

            /// <summary>
            /// Gets if the polyline is closed.
            /// </summary>
            public bool IsClosed;

            /// <summary>
            /// Initializes a new instance of the <c>HatchBoundaryPath.Polyline</c> class.
            /// </summary>
            public Polyline()
                : base(EdgeType.Polyline)
            {
                this.IsClosed = true;
            }

            /// <summary>
            /// Initializes a new instance of the <c>HatchBoundaryPath.Polyline</c> class.
            /// </summary>
            /// <param name="entity"><see cref="EntityObject">Entity</see> that represents the edge.</param>
            public Polyline(EntityObject entity)
                : base(EdgeType.Polyline)
            {
                if (entity == null)
                {
                    throw new ArgumentNullException(nameof(entity));
                }

                //if (entity.Type == EntityType.Polyline2D)
                //{
                //    Entities.Polyline2D poly = (Entities.Polyline2D)entity;
                //    this.IsClosed = poly.IsClosed;
                //    this.Vertexes = new Vector3[poly.Vertexes.Count];
                //    for (int i = 0; i < poly.Vertexes.Count; i++)
                //    {
                //        this.Vertexes[i] = new Vector3(poly.Vertexes[i].Position.X, poly.Vertexes[i].Position.Y, poly.Vertexes[i].Bulge);
                //    }
                //}
                //else if (entity.Type == EntityType.Polyline3D)
                //{
                //    Matrix3 trans = MathHelper.ArbitraryAxis(entity.Normal).Transpose();

                //    Entities.Polyline3D poly = (Entities.Polyline3D)entity;
                //    this.IsClosed = poly.IsClosed;
                //    this.Vertexes = new Vector3[poly.Vertexes.Count];
                //    for (int i = 0; i < poly.Vertexes.Count; i++)
                //    {
                //        Vector3 point = trans * poly.Vertexes[i];
                //        this.Vertexes[i] = new Vector3(point.X, point.Y, 0.0);
                //    }
                //}
                //else
                //    throw new ArgumentException("The entity is not a Polyline2D or a Polyline3D", nameof(entity));
            }

            /// <summary>
            /// Initializes a new instance of the <c>HatchBoundaryPath.Polyline</c> class.
            /// </summary>
            /// <param name="entity"><see cref="EntityObject">Entity</see> that represents the edge.</param>
            public static Polyline ConvertFrom(EntityObject entity)
            {
                return new Polyline(entity);
            }

            /// <summary>
            /// Converts the actual edge to its entity equivalent.
            /// </summary>
            /// <returns>An <see cref="EntityObject">entity</see> equivalent to the actual edge.</returns>
            public override EntityObject ConvertTo()
            {
                //List<Polyline2DVertex> points = new List<Polyline2DVertex>(this.Vertexes.Length);
                //foreach (Vector3 point in this.Vertexes)
                //{
                //    points.Add(new Polyline2DVertex(point.X, point.Y, point.Z));
                //}
                //return new Entities.Polyline2D(points, this.IsClosed);
                return null;
            }

            /// <summary>
            /// Clones the actual edge.
            /// </summary>
            /// <returns>A copy of the actual edge.</returns>
            public override object Clone()
            {
                Polyline copy = new Polyline
                {
                    Vertexes = new Vector2d[this.Vertexes.Length]
                };

                for (int i = 0; i < this.Vertexes.Length; i++)
                {
                    copy.Vertexes[i] = this.Vertexes[i];
                }
                return copy;
            }
        }

        /// <summary>
        /// Represents a line edge of a HatchBoundaryPath.
        /// </summary>
        public class Line :
            Edge
        {
            /// <summary>
            /// Gets or sets the start point of the line.
            /// </summary>
            public Vector2d Start;

            /// <summary>
            /// Gets or sets the end point of the line.
            /// </summary>
            public Vector2d End;

            /// <summary>
            /// Initializes a new instance of the <c>HatchBoundaryPath.Line</c> class.
            /// </summary>
            public Line()
                : base(EdgeType.Line)
            {
            }

            /// <summary>
            /// Initializes a new instance of the <c>HatchBoundaryPath.Line</c> class.
            /// </summary>
            /// <param name="entity"><see cref="EntityObject">Entity</see> that represents the edge.</param>
            public Line(EntityObject entity)
                : base(EdgeType.Line)
            {
                if (entity == null)
                    throw new ArgumentNullException(nameof(entity));

                //Entities.Line line = entity as Entities.Line;
                //if (line == null)
                //    throw new ArgumentException("The entity is not a Line", nameof(entity));

                //Vector2d point;
                //Matrix3 trans = MathHelper.ArbitraryAxis(entity.Normal).Transpose();

                //point = trans * line.StartPoint;
                //this.Start = new Vector2d(point.X, point.Y);
                //point = trans * line.EndPoint;
                //this.End = new Vector2d(point.X, point.Y);
            }

            /// <summary>
            /// Creates a BoundaryBoundaryPath from an <see cref="EntityObject">entity</see>.
            /// </summary>
            /// <param name="entity">An <see cref="EntityObject">entity</see>.</param>
            /// <returns>A HatchBoundaryPath line.</returns>
            public static Line ConvertFrom(EntityObject entity)
            {
                return new Line(entity);
            }

            /// <summary>
            /// Converts the actual edge to its entity equivalent.
            /// </summary>
            /// <returns>An <see cref="EntityObject">entity</see> equivalent to the actual edge.</returns>
            public override EntityObject ConvertTo()
            {
                return null;// new Entities.Line(this.Start, this.End);
            }

            /// <summary>
            /// Clones the actual edge.
            /// </summary>
            /// <returns>A copy of the actual edge.</returns>
            public override object Clone()
            {
                Line copy = new Line
                {
                    Start = this.Start,
                    End = this.End
                };

                return copy;
            }
        }

        /// <summary>
        /// Represents an arc edge of a HatchBoundaryPath.
        /// </summary>
        public class Arc :
            Edge
        {
            /// <summary>
            /// Gets or set the center of the arc.
            /// </summary>
            public Vector2d Center;

            /// <summary>
            /// Gets or sets the radius of the arc.
            /// </summary>
            public double Radius;

            /// <summary>
            /// Gets or sets the start angle of the arc.
            /// </summary>
            public double StartAngle;

            /// <summary>
            /// Gets or sets the end angle of the arc.
            /// </summary>
            public double EndAngle;

            /// <summary>
            /// Gets or sets if the arc is counter clockwise.
            /// </summary>
            public bool IsCounterclockwise;

            /// <summary>
            /// Initializes a new instance of the <c>HatchBoundaryPath.Arc</c> class.
            /// </summary>
            public Arc()
                : base(EdgeType.Arc)
            {
            }

            /// <summary>
            /// Initializes a new instance of the <c>HatchBoundaryPath.Arc</c> class.
            /// </summary>
            /// <param name="entity"><see cref="EntityObject">Entity</see> that represents the edge.</param>
            public Arc(EntityObject entity)
                : base(EdgeType.Arc)
            {
                if (entity == null)
                    throw new ArgumentNullException(nameof(entity));
                Vector2d point;
                //Matrix3 trans = MathHelper.ArbitraryAxis(entity.Normal).Transpose();
                //switch (entity.Type)
                //{
                //    case EntityType.Arc:
                //        Entities.Arc arc = (Entities.Arc)entity;
                //        point = trans * arc.Center;
                //        this.Center = new Vector2(point.X, point.Y);
                //        this.Radius = arc.Radius;
                //        this.StartAngle = arc.StartAngle;
                //        this.EndAngle = arc.EndAngle;
                //        this.IsCounterclockwise = true;
                //        break;
                //    case EntityType.Circle:
                //        Entities.Circle circle = (Circle)entity;
                //        point = trans * circle.Center;
                //        this.Center = new Vector2(point.X, point.Y);
                //        this.Radius = circle.Radius;
                //        this.StartAngle = 0.0;
                //        this.EndAngle = 360.0;
                //        this.IsCounterclockwise = true;
                //        break;
                //    default:
                //        throw new ArgumentException("The entity is not a Circle or an Arc", nameof(entity));
                //}
            }

            /// <summary>
            /// Initializes a new instance of the <c>HatchBoundaryPath.Arc</c> class.
            /// </summary>
            /// <param name="entity"><see cref="EntityObject">Entity</see> that represents the edge.</param>
            public static Arc ConvertFrom(EntityObject entity)
            {
                return new Arc(entity);
            }

            /// <summary>
            /// Converts the actual edge to its entity equivalent.
            /// </summary>
            /// <returns>An <see cref="EntityObject">entity</see> equivalent to the actual edge.</returns>
            public override EntityObject ConvertTo()
            {
                //    if (MathHelper.IsEqual(MathHelper.NormalizeAngle(this.StartAngle), MathHelper.NormalizeAngle(this.EndAngle)))
                //    {
                //        return new Entities.Circle(this.Center, this.Radius);
                //    }

                //    if (this.IsCounterclockwise)
                //    {
                //        return new Entities.Arc(this.Center, this.Radius, this.StartAngle, this.EndAngle);
                //    }

                //    return new Entities.Arc(this.Center, this.Radius, 360 - this.EndAngle, 360 - this.StartAngle);
                return null;
            }

            /// <summary>
            /// Clones the actual edge.
            /// </summary>
            /// <returns>A copy of the actual edge.</returns>
            public override object Clone()
            {
                Arc copy = new Arc
                {
                    Center = this.Center,
                    Radius = this.Radius,
                    StartAngle = this.StartAngle,
                    EndAngle = this.EndAngle,
                    IsCounterclockwise = this.IsCounterclockwise
                };

                return copy;
            }
        }

        /// <summary>
        /// Represents a ellipse edge of a HatchBoundaryPath.
        /// </summary>
        public class Ellipse :
            Edge
        {
            /// <summary>
            /// Gets or sets the center of the ellipse.
            /// </summary>
            public Vector2d Center;

            /// <summary>
            /// Gets or sets the position of the end of the major axis.
            /// </summary>
            public Vector2d EndMajorAxis;

            /// <summary>
            /// Gets or sets the scale of the minor axis in respect of the major axis.
            /// </summary>
            public double MinorRatio;

            /// <summary>
            /// Gets or sets the start angle of the ellipse.
            /// </summary>
            public double StartAngle;

            /// <summary>
            /// Gets or sets the end angle of the ellipse.
            /// </summary>
            public double EndAngle;

            /// <summary>
            /// Gets or sets if the ellipse is counter clockwise.
            /// </summary>
            public bool IsCounterclockwise;

            /// <summary>
            /// Initializes a new instance of the <c>HatchBoundaryPath.Ellipse</c> class.
            /// </summary>
            public Ellipse()
                : base(EdgeType.Ellipse)
            {
            }

            /// <summary>
            /// Initializes a new instance of the <c>HatchBoundaryPath.Ellipse</c> class.
            /// </summary>
            /// <param name="entity"><see cref="EntityObject">Entity</see> that represents the edge.</param>
            public Ellipse(EntityObject entity)
                : base(EdgeType.Ellipse)
            {
                if (entity == null)
                    throw new ArgumentNullException(nameof(entity));

                //Entities.Ellipse ellipse = entity as Entities.Ellipse;
                //if (ellipse == null)
                //    throw new ArgumentException("The entity is not an Ellipse", nameof(entity));

                //Matrix3 trans = MathHelper.ArbitraryAxis(entity.Normal).Transpose();

                //Vector3 point = trans * ellipse.Center;
                //this.Center = new Vector2(point.X, point.Y);

                //double sine = 0.5 * ellipse.MajorAxis * Math.Sin(ellipse.Rotation * MathHelper.DegToRad);
                //double cosine = 0.5 * ellipse.MajorAxis * Math.Cos(ellipse.Rotation * MathHelper.DegToRad);
                //this.EndMajorAxis = new Vector2(cosine, sine);
                //this.MinorRatio = ellipse.MinorAxis / ellipse.MajorAxis;
                //if (ellipse.IsFullEllipse)
                //{
                //    this.StartAngle = 0.0;
                //    this.EndAngle = 360.0;
                //}
                //else
                //{
                //    this.StartAngle = ellipse.StartAngle;
                //    this.EndAngle = ellipse.EndAngle;
                //}
                //this.IsCounterclockwise = true;
            }

            /// <summary>
            /// Initializes a new instance of the <c>HatchBoundaryPath.Ellipse</c> class.
            /// </summary>
            /// <param name="entity"><see cref="EntityObject">Entity</see> that represents the edge.</param>
            public static Ellipse ConvertFrom(EntityObject entity)
            {
                return new Ellipse(entity);
            }

            /// <summary>
            /// Converts the actual edge to its entity equivalent.
            /// </summary>
            /// <returns>An <see cref="EntityObject">entity</see> equivalent to the actual edge.</returns>
            public override EntityObject ConvertTo()
            {
                //Vector3 center = new Vector3(this.Center.X, this.Center.Y, 0.0);
                //Vector3 axisPoint = new Vector3(this.EndMajorAxis.X, this.EndMajorAxis.Y, 0.0);
                //Vector3 ocsAxisPoint = MathHelper.Transform(axisPoint,
                //    Vector3.UnitZ,
                //    CoordinateSystem.World,
                //    CoordinateSystem.Object);
                //double rotation = Vector2.Angle(new Vector2(ocsAxisPoint.X, ocsAxisPoint.Y)) * MathHelper.RadToDeg;
                //double majorAxis = 2 * axisPoint.Modulus();
                //return new Entities.Ellipse(center, majorAxis, majorAxis * this.MinorRatio)
                //{
                //    Rotation = rotation,
                //    StartAngle = this.IsCounterclockwise ? this.StartAngle : 360 - this.EndAngle,
                //    EndAngle = this.IsCounterclockwise ? this.EndAngle : 360 - this.StartAngle,
                //};
                return null;
            }

            /// <summary>
            /// Clones the actual edge.
            /// </summary>
            /// <returns>A copy of the actual edge.</returns>
            public override object Clone()
            {
                Ellipse copy = new Ellipse
                {
                    Center = this.Center,
                    EndMajorAxis = this.EndMajorAxis,
                    MinorRatio = this.MinorRatio,
                    StartAngle = this.StartAngle,
                    EndAngle = this.EndAngle,
                    IsCounterclockwise = this.IsCounterclockwise
                };

                return copy;
            }
        }

        /// <summary>
        /// Represents a spline edge of a HatchBoundaryPath.
        /// </summary>
        public class Spline :
            Edge
        {
            /// <summary>
            /// Gets or sets the degree of the spline
            /// </summary>
            public short Degree;

            /// <summary>
            /// Gets or sets if the spline is rational.
            /// </summary>
            public bool IsRational;

            /// <summary>
            /// Gets or sets if the spline is periodic.
            /// </summary>
            public bool IsPeriodic;

            /// <summary>
            /// Gets or sets the list of knots of the spline.
            /// </summary>
            public double[] Knots;

            /// <summary>
            /// Gets or sets the list of control points of the spline.
            /// </summary>
            /// <remarks>
            /// The position of the control point is defined by the X and Y coordinates, the Z value represents its weight.
            /// </remarks>
            public Vector2d[] ControlPoints; // location: (x, y) weight: z

            /// <summary>
            /// Initializes a new instance of the <c>HatchBoundaryPath.Spline</c> class.
            /// </summary>
            public Spline()
                : base(EdgeType.Spline)
            {
            }

            /// <summary>
            /// Initializes a new instance of the <c>HatchBoundaryPath.Spline</c> class.
            /// </summary>
            /// <param name="entity"><see cref="EntityObject">Entity</see> that represents the edge.</param>
            public Spline(EntityObject entity)
                : base(EdgeType.Spline)
            {
                //if (entity == null)
                //{
                //    throw new ArgumentNullException(nameof(entity));
                //}

                //Entities.Spline spline = entity as Entities.Spline;
                //if (spline == null)
                //{
                //    throw new ArgumentException("The entity is not an Spline", nameof(entity));
                //}

                //this.Degree = spline.Degree;
                //this.IsRational = true;
                //this.IsPeriodic = spline.IsClosedPeriodic;
                //if (spline.ControlPoints.Length == 0)
                //{
                //    throw new ArgumentException("The HatchBoundaryPath spline edge requires a spline entity with control points.", nameof(entity));
                //}

                //Matrix3 trans = MathHelper.ArbitraryAxis(entity.Normal).Transpose();

                //this.ControlPoints = new Vector3[spline.ControlPoints.Length];
                //for (int i = 0; i < spline.ControlPoints.Length; i++)
                //{
                //    Vector3 point = trans * spline.ControlPoints[i];
                //    this.ControlPoints[i] = new Vector3(point.X, point.Y, spline.Weights[i]);
                //}

                //this.Knots = new double[spline.Knots.Length];
                //for (int i = 0; i < spline.Knots.Length; i++)
                //{
                //    this.Knots[i] = spline.Knots[i];
                //}
            }

            /// <summary>
            /// Initializes a new instance of the <c>HatchBoundaryPath.Spline</c> class.
            /// </summary>
            /// <param name="entity"><see cref="EntityObject">Entity</see> that represents the edge.</param>
            public static Spline ConvertFrom(EntityObject entity)
            {
                return new Spline(entity);
            }

            /// <summary>
            /// Converts the actual edge to its entity equivalent.
            /// </summary>
            /// <returns>An <see cref="EntityObject">entity</see> equivalent to the actual edge.</returns>
            public override EntityObject ConvertTo()
            {
                //List<Vector3> ctrl = new List<Vector3>();
                //List<double> weights = new List<double>();
                //List<double> knots = new List<double>(this.Knots);

                //foreach (Vector3 point in this.ControlPoints)
                //{
                //    ctrl.Add(new Vector3(point.X, point.Y, 0.0));
                //    weights.Add(point.Z);
                //}
                return null;// new Entities.Spline(ctrl, weights, knots, this.Degree, this.IsPeriodic);
            }

            /// <summary>
            /// Clones the actual edge.
            /// </summary>
            /// <returns>A copy of the actual edge.</returns>
            public override object Clone()
            {
                Spline copy = new Spline
                {
                    Degree = this.Degree,
                    IsRational = this.IsRational,
                    IsPeriodic = this.IsPeriodic,
                    Knots = new double[this.Knots.Length],
                    //ControlPoints = new Vector3[this.ControlPoints.Length],
                };
                for (int i = 0; i < this.Knots.Length; i++)
                {
                    copy.Knots[i] = this.Knots[i];
                }
                for (int i = 0; i < this.ControlPoints.Length; i++)
                {
                    copy.ControlPoints[i] = this.ControlPoints[i];
                }
                return copy;
            }
        }

        #endregion

        #region private fields

        private readonly List<EntityObject> entities;
        private readonly List<Edge> edges;
        private HatchBoundaryPathTypeFlags pathType;

        #endregion

        #region constructor

        /// <summary>
        /// Initializes a new instance of the <c>Hatch</c> class.
        /// </summary>
        /// <param name="edges">List of entities that makes a loop for the hatch boundary paths.</param>
        public HatchBoundaryPath(IEnumerable<EntityObject> edges)
        {
            if (edges == null)
            {
                throw new ArgumentNullException(nameof(edges));
            }
            this.edges = new List<Edge>();
            this.pathType = HatchBoundaryPathTypeFlags.Derived | HatchBoundaryPathTypeFlags.External;
            this.entities = new List<EntityObject>(edges);
            this.Update();
        }

        internal HatchBoundaryPath(IEnumerable<Edge> edges)
        {
            if (edges == null)
            {
                throw new ArgumentNullException(nameof(edges));
            }
            this.pathType = HatchBoundaryPathTypeFlags.Derived | HatchBoundaryPathTypeFlags.External;
            this.entities = new List<EntityObject>();
            this.edges = new List<Edge>(edges);
            if (this.edges.Count == 1 && this.edges[0].Type == EdgeType.Polyline)
            {
                this.pathType |= HatchBoundaryPathTypeFlags.Polyline;
            }
            else
            {
                foreach (Edge edge in this.edges)
                {
                    if (edge.Type == EdgeType.Polyline) throw new ArgumentException("Only a single polyline edge can be part of a HatchBoundaryPath.", nameof(edges));
                }
            }
        }

        #endregion

        #region public properties

        /// <summary>
        /// Gets the list of edges that makes a loop for the hatch boundary path.
        /// </summary>
        public IReadOnlyList<Edge> Edges
        {
            get { return this.edges; }
        }

        /// <summary>
        /// Gets the boundary path type flag.
        /// </summary>
        public HatchBoundaryPathTypeFlags PathType
        {
            get { return this.pathType; }
            internal set { this.pathType = value; }
        }

        /// <summary>
        /// Gets the list of entities that makes the boundary.
        /// </summary>
        /// <remarks>If the boundary path belongs to a non-associative hatch this list will contain zero entities.</remarks>
        public IReadOnlyList<EntityObject> Entities
        {
            get { return this.entities; }
        }

        #endregion

        #region internal methods

        internal void AddContour(EntityObject entity)
        {
            this.entities.Add(entity);
        }

        internal void ClearContour()
        {
            this.entities.Clear();
        }

        internal bool RemoveContour(EntityObject entity)
        {
            return this.entities.Remove(entity);
        }

        #endregion

        #region public methods

        /// <summary>
        /// Updates the internal HatchBoundaryPath data. 
        /// </summary>
        /// <remarks>
        /// It is necessary to manually call this method when changes to the boundary entities are made. This is only applicable to associative hatches,
        /// non-associative hatches has no associated boundary entities.
        /// </remarks>
        public void Update()
        {
            this.SetInternalInfo(this.entities, true);
        }

        #endregion

        #region private methods

        private void SetInternalInfo(IEnumerable<EntityObject> contour, bool clearEdges)
        {
            bool containsPolyline = false;
            if (clearEdges)
            {
                this.edges.Clear();
            }

            foreach (EntityObject entity in contour)
            {
                if (containsPolyline)
                {
                    throw new ArgumentException("Closed polylines cannot be combined with other entities to make a hatch boundary path.");
                }

                // it seems that AutoCad does not have problems on creating loops that theoretically does not make sense,
                // like, for example, an internal loop that is made of a single arc.
                // so if AutoCAD is OK with that I am too, the program that make use of this information will take care of this inconsistencies
                //switch (entity.Type)
                //{
                //    case EntityType.Arc:
                //        this.edges.Add(Arc.ConvertFrom(entity));
                //        break;
                //    case EntityType.Circle:
                //        this.edges.Add(Arc.ConvertFrom(entity));
                //        break;
                //    case EntityType.Ellipse:
                //        this.edges.Add(Ellipse.ConvertFrom(entity));
                //        break;
                //    case EntityType.Line:
                //        this.edges.Add(Line.ConvertFrom(entity));
                //        break;
                //    case EntityType.Polyline2D:
                //        Entities.Polyline2D lwpoly = (Entities.Polyline2D)entity;
                //        if (lwpoly.IsClosed)
                //        {
                //            if (this.edges.Count != 0)
                //            {
                //                throw new ArgumentException("Closed polylines cannot be combined with other entities to make a hatch boundary path.");
                //            }
                //            this.edges.Add(Polyline.ConvertFrom(entity));
                //            this.pathType |= HatchBoundaryPathTypeFlags.Polyline;
                //            containsPolyline = true;
                //        }
                //        else
                //            this.SetInternalInfo(lwpoly.Explode(), false); // open polylines will always be exploded, only one polyline can be present in a path
                //        break;
                //    case EntityType.Polyline3D:
                //        Entities.Polyline3D poly = (Entities.Polyline3D)entity;
                //        if (poly.IsClosed)
                //        {
                //            if (this.edges.Count != 0)
                //            {
                //                throw new ArgumentException("Closed polylines cannot be combined with other entities to make a hatch boundary path.");
                //            }
                //            this.edges.Add(Polyline.ConvertFrom(entity));
                //            this.pathType |= HatchBoundaryPathTypeFlags.Polyline;
                //            containsPolyline = true;
                //        }
                //        else
                //            this.SetInternalInfo(poly.Explode(), false); // open polylines will always be exploded, only one polyline can be present in a path
                //        break;
                //    case EntityType.Spline:
                //        this.edges.Add(Spline.ConvertFrom(entity));
                //        break;
                //    default:
                //        throw new ArgumentException(string.Format("The entity type {0} cannot be part of a hatch boundary. Only Arc, Circle, Ellipse, Line, Polyline2D, Polyline3D, and Spline entities are allowed.", entity.Type));
                //}
            }
        }

        #endregion

        #region ICloneable

        /// <summary>
        /// Creates a new HatchBoundaryPath that is a copy of the current instance.
        /// </summary>
        /// <returns>A new HatchBoundaryPath that is a copy of this instance.</returns>
        /// <remarks>When cloning a HatchBoundaryPath, if it has entities that defines its contour, they will not be cloned.</remarks>
        public object Clone()
        {
            List<Edge> copyEdges = new List<Edge>();
            foreach (Edge edge in this.edges)
            {
                copyEdges.Add((Edge)edge.Clone());
            }

            return new HatchBoundaryPath(copyEdges);
        }

        #endregion
    }
}
